/*
Copyright 2018 Embedded Microprocessor Benchmark Consortium (EEMBC)

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Original Author: Shay Gal-on
*/

/* File: core_main.c
        This file contains the framework to acquire a block of memory, seed
   initial parameters, tun t he benchmark and report the results.
*/
#include "coremark.h"

void *
iterate(void *pres)
{
    ee_u32        i;
    ee_u16        crc;
    core_results *res        = (core_results *)pres;
    ee_u32        iterations = res->iterations;
    res->crc                 = 0;
    res->crclist             = 0;
    res->crcmatrix           = 0;
    res->crcstate            = 0;

    for (i = 0; i < iterations; i++)
    {
        crc      = core_bench_list(res, 1);
        res->crc = crcu16(crc, res->crc);
        crc      = core_bench_list(res, -1);
        res->crc = crcu16(crc, res->crc);
        if (i == 0)
            res->crclist = res->crc;
    }
    return NULL;
}

int benchmarks_score(int thread, int iterations){
    if(thread <= 0 || thread > 1025){ thread = 1; }
    if(iterations < 0 || iterations > 1000000){ iterations = 0; }
    ee_printf("Thread is %d, iterations is %d!\n", thread, iterations);
    ee_u16       i, j = 0, num_algorithms = 0;
    CORE_TICKS   total_time;
    core_results results[thread];
    ee_u8 stack_memblock[TOTAL_DATA_SIZE * thread];
    if (sizeof(struct list_head_s) > 128) {
        ee_printf("list_head structure too big for comparable data!\n");
        return -1;
    }
    results[0].seed1      = 0;
    results[0].seed2      = 0;
    results[0].seed3      = 0;
    results[0].iterations = iterations;
    results[0].execs = ALL_ALGORITHMS_MASK;

    for (i = 0; i < thread; i++) {
        results[i].memblock[0] = stack_memblock + i * TOTAL_DATA_SIZE;
        results[i].size        = TOTAL_DATA_SIZE;
        results[i].seed1       = results[0].seed1;
        results[i].seed2       = results[0].seed2;
        results[i].seed3       = results[0].seed3;
        results[i].err         = 0;
        results[i].execs       = results[0].execs;
    }
    /* Data init */
    /* Find out how space much we have based on number of algorithms */
    for (i = 0; i < NUM_ALGORITHMS; i++) {
        if ((1 << (ee_u32)i) & results[0].execs)
            num_algorithms++;
    }
    for (i = 0; i < thread; i++)
        results[i].size = results[i].size / num_algorithms;
    /* Assign pointers */
    for (i = 0; i < NUM_ALGORITHMS; i++){
        ee_u32 ctx;
        if ((1 << (ee_u32)i) & results[0].execs) {
            for (ctx = 0; ctx < thread; ctx++)
                results[ctx].memblock[i + 1] = (char *)(results[ctx].memblock[0]) + results[0].size * j;
            j++;
        }
    }
    /* call inits */
    for (i = 0; i < thread; i++) {
        if (results[i].execs & ID_LIST){
            results[i].list = core_list_init(
                results[0].size, results[i].memblock[1], results[i].seed1);
        }
        if (results[i].execs & ID_MATRIX){
            core_init_matrix(results[0].size,
                             results[i].memblock[2],
                             (ee_s32)results[i].seed1
                                 | (((ee_s32)results[i].seed2) << 16),
                             &(results[i].mat));
        }
        if (results[i].execs & ID_STATE){
            core_init_state(results[0].size, results[i].seed1, results[i].memblock[3]);
        }
    }

    /* automatically determine number of iterations if not set */
    if (results[0].iterations == 0){
        secs_ret secs_passed = 0;
        ee_u32   divisor;
        results[0].iterations = 1;
        while (secs_passed < (secs_ret)1){
            results[0].iterations *= 10;
            start_time();
            iterate(&results[0]);
            stop_time();
            secs_passed = time_in_secs(get_time());
        }
        /* now we know it executes for at least 1 sec, set actual run time at
         * about 10 secs */
        divisor = (ee_u32)secs_passed;
        if (divisor == 0) /* some machines cast float to int as 0 since this
                             conversion is not defined by ANSI, but we know at
                             least one second passed */
            divisor = 1;
        results[0].iterations *= 1 + 10 / divisor;
    }
    /* perform actual benchmark */
    start_time();

    if(thread > 1){
        for (i = 0; i < thread; i++){
            results[i].iterations = results[0].iterations;
            results[i].execs      = results[0].execs;
            core_start_parallel(&results[i]);
        }
        for (i = 0; i < thread; i++){
            core_stop_parallel(&results[i]);
        }
    }else{
        iterate(&results[0]);
    }

    stop_time();
    total_time = get_time();
    float score = thread * results[0].iterations / time_in_secs(total_time);
    ee_printf("Total ticks      : %lu\n", (long unsigned)total_time);
    ee_printf("Total time (secs): %f\n", time_in_secs(total_time));
    ee_printf("Iterations/Sec   : %f\n", score);
    return (int)score;
}

int main(int argc, char *argv[]){
    benchmarks_score(0, 0);
    return 0;
}
